﻿using Super_Management_Mayhem.Content.Spritesheet;
using Super_Management_Mayhem.Control;
using Super_Management_Mayhem.Screens.Playing.AttackModel;
using Super_Management_Mayhem.Screens.Playing.ObjectInterfaces;
using Super_Management_Mayhem.Screens.Playing.Particle;
using System.Linq;
using Transformable_Engine_v2.Engine.GameObjects.Containers;
using Transformable_Engine_v2.Engine.Graphics;
using Transformable_Engine_v2.Engine.Helper;
using Transformable_Engine_v2.Engine.Time;
using Transformable_Engine_v2.Engine.Transformable_Math.Geometry;

namespace Super_Management_Mayhem.Screens.Playing.Players
{
    public sealed class PlayerContainer : Container, IOrderable, IAttackable, IAttacker
    {
        private Manager.Manager _player;
        private readonly Hud.Hud _hud;

        private int _points;
        private const int STARTING_HEALTH = 20;
        private int CurrentHealth { get; set; }

        private readonly InputKeys _input;

        private readonly Timer _deathTimer;

        private readonly Team _team;

        private readonly ItemSheet.Classes[] _currentClasses;

        IAttacker IAttacker.GetReference()
        {
            return this;
        }

        public new TE_Vector2 GetPosition()
        {
            return _player.PlayerPosition;
        }

        public void OnAttack()
        {

        }

        public Team GetTeam()
        {
            return _team;
        }

        public TE_Vector2 CameraPosition
        {
            get
            {
                return _player.CameraPosition;
            }
        }

        public TE_Rectangle ItemBoundingBox
        {
            get
            {
                return _player.ItemBoundingBox;
            }
        }

        

        public bool Alive
        {
            get { return CurrentHealth > 0; }
        }

        public PlayerContainer(TE_Vector2 position,
                               string playerName,
                               InputKeys input,
                               TE_Vector2 hudPosition,
                               TE_Vector2 hudSize,
                               Team team)
        {
            _currentClasses = new ItemSheet.Classes[3]
                                  {
                                      ItemSheet.Classes.Unknown,
                                      ItemSheet.Classes.Unknown, 
                                      ItemSheet.Classes.Unknown
                                  };

            _points = Playscreen.Map.StartingPoints;

            _team = team;

            _input = input;

            _player = new Manager.Manager(position, _team.GetColor(), input, this, null);

            CurrentHealth = STARTING_HEALTH;

            _hud = new Hud.Hud(hudSize, playerName, _team.GetColor(), STARTING_HEALTH, _points) { PositionGridBound = new TE_Vector2(hudPosition) };
            _hud.ChangeFace(_player.GetFaceIndicator());

            AddTimers(_deathTimer = new Timer(4000, false, RespawnPlayer));

            AddChildren(_player, _hud);
        }

        private void RespawnPlayer()
        {
            var players = Playscreen.Map.Players.Where(x => x != this).ToList();
            var spawnPoints = Playscreen.Map.GetSpawnPoints();
            double maxDistance = 0;
            int maxIndex = 0;

            //Calculate most favorable spawn point
            for (int i = 0; i < spawnPoints.Count(); i++)
            {
                double currentDistance = players.Sum(player => (spawnPoints[i].ToGridbound() - player.GetPosition()).Length);

                if (currentDistance > maxDistance)
                {
                    maxDistance = currentDistance;
                    maxIndex = i;
                }
            }

            //Spawn player
            var cameraPosition = CameraPosition;
            _player.Die();

            _player = new Manager.Manager(spawnPoints[maxIndex], _team.GetColor(), _input, this, cameraPosition);

            AddChildren(_player);

            _hud.ChangeFace(_player.GetFaceIndicator());

            CurrentHealth = STARTING_HEALTH;
            _hud.ChangeHealth(CurrentHealth);
        }


        public TE_Rectangle GetBoundingBoxWall()
        {
            return _player.BoundingBoxWall;
        }

        public TE_Rectangle GetBoundingBoxCollect()
        {
            return _player.BoundingBoxCollect;
        }

        public TE_Rectangle GetBoundingBoxCollision()
        {
            return _player.BoundingBoxCollision;
        }

        public float GetOrderingFactor()
        {
            return GetPosition().Y;
        }

        /// <summary>
        /// Method used to avoid a player hitting himself while attacking
        /// </summary>
        /// <returns></returns>
        public Container GetReference()
        {
            return _player;
        }

        public bool Hit(TE_Vector2 direction, TE_Vector2 attackerPosition, int hurtTime, int damage)
        {
            return _player.Hit(direction, attackerPosition, hurtTime, damage);
        }

        /// <summary>
        /// When hit, scatter 10% of your points around the map
        /// </summary>
        public void LosePoints(int damage, TE_Rectangle holeSize = null)
        {
            CurrentHealth -= damage;
            if (CurrentHealth < 0)
                CurrentHealth = 0;

            _hud.ChangeHealth(CurrentHealth);

            int pointsLost = 0;
            
            if (CurrentHealth > 0)
                pointsLost = (int) (_points*0.1f);
            else
            {
                pointsLost = (int)(_points * 0.3f);
                _deathTimer.Start();
            }
                

            _points -= pointsLost;

            _hud.ChangeScore(_points, -1);

            Playscreen.Map.TossPoints(pointsLost, new TE_Vector2(_player.PlayerPosition), holeSize);
        }

        public void CollectPoint(int value)
        {
            _points += value;
            _hud.ChangeScore(_points, value / 10.0f);
        }

        public override void Draw()
        {
            if(_player.IsAlive())
                _player.Draw();
        }

        public void DrawHud()
        {
            _hud.Draw();
        }

        public void LoseAllLife(TE_Rectangle holeSize)
        {
            CurrentHealth = 1;
            LosePoints(1, holeSize);
        }

        public void ChangeClass(ItemSheet.Classes nextClass)
        {
            var position = _player.PlayerPosition.AsGridBound().Offset(-0.5f, -0.5f);
            var cameraPosition = CameraPosition;
            _player.Die();


            ItemSheet.Classes gottenClass = nextClass;
            if (nextClass == ItemSheet.Classes.Random)
                gottenClass = (ItemSheet.Classes) Randomizer.Int((int) ItemSheet.Classes.Power, (int) ItemSheet.Classes.Utility);

            if (_currentClasses.Any(x => x == ItemSheet.Classes.Unknown))
            {
                for (int i = 0; i < 3; i++)
                {
                    if (_currentClasses[i] == ItemSheet.Classes.Unknown)
                    {
                        _currentClasses[i] = gottenClass;
                        break;
                    }
                }
            }
            else
            {
                ClearClasses();
                _currentClasses[0] = gottenClass;
            }

            _hud.ChangeClass(_currentClasses);

            string nextValue = ((int)_currentClasses[0] * 100 + (int)_currentClasses[1] * 10 + (int)_currentClasses[2] * 1).ToString().Replace("3", "7");

            switch (nextValue)
            {
                case "277":
                case "727":
                case "772":
                    _player = new Native.Native(position, _team.GetColor(), _input, this, cameraPosition, Native.Native.NativeType.Regular);
                    break;
                case "272":
                case "227":
                case "722":
                    _player = new Native.Native(position, _team.GetColor(), _input, this, cameraPosition, Native.Native.NativeType.Backslide);
                    break;
                case "222":
                    _player = new Native.Native(position, _team.GetColor(), _input, this, cameraPosition, Native.Native.NativeType.TriArrow);
                    break;
                default:
                    _player = new Manager.Manager(position, _team.GetColor(), _input, this, cameraPosition);
                    break;
            }

            AddChildren(_player);

            _hud.ChangeFace(_player.GetFaceIndicator());

            Playscreen.Map.AddParticle(new ChangeClassSmoke { PositionGridBound = position.Offset(0.5f, - 1 + 0.5f) });
        }

        void ClearClasses()
        {
            for (int i = 0; i < 3; i++)
            {
                _currentClasses[i] = ItemSheet.Classes.Unknown;
            }
        }

        public void TimesUp()
        {
            _hud.TimesUp();
        }

        public int GetPoints()
        {
            return _points;
        }
    }
}
